let print_dict_entry (k, v) =
  print_int k ; print_newline () ; print_string v ; print_newline ()

let print_dict_entry (k, v) =
  print_int k;
  print_newline ();
  print_string v;
  print_newline ()

let rec print_dict d =
  match d with
    [] -> ()
  | h::t -> print_dict_entry h; print_dict t

let rec iter f l =
  match l with
    [] -> ()
  | h::t -> f h; iter f t

let print_dict d =
  iter print_dict_entry d

let print_dict =
  iter print_dict_entry

let rec read_dict () =
  let i = read_int () in
    if i = 0 then [] else
      let name = read_line () in
        (i, name) :: read_dict ()

let rec read_dict () =
  try
    let i = read_int () in
      if i = 0 then [] else
        let name = read_line () in
          (i, name) :: read_dict ()
  with
    Failure "int_of_string" ->
      print_string "This is not a valid integer. Please try again.";
      print_newline ();
      read_dict ()

let entry_to_channel ch (k, v) =
  output_string ch (string_of_int k);
  output_char ch '\n';
  output_string ch v;
  output_char ch '\n'

let dictionary_to_channel ch d =
  iter (entry_to_channel ch) d

let dictionary_to_file filename dict =
  let ch = open_out filename in
    dictionary_to_channel ch dict;
    close_out ch

let entry_of_channel ch =
  let number = input_line ch in
    let name = input_line ch in
      (int_of_string number, name)

let rec dictionary_of_channel ch =
  try
    let e = entry_of_channel ch in
      e :: dictionary_of_channel ch
  with
    End_of_file -> []

let dictionary_of_file filename =
  let ch = open_in filename in
    let dict = dictionary_of_channel ch in
      close_in ch;
      dict


(* q1 *)
let print_int_list l = 
  "[" ^
  (List.map string_of_int l |>
  String.concat "; ")
  ^ "]"

(* q2 *)
let read_three_int () = 
  try
    let i1 = read_int () in
    let i2 = read_int () in
    let i3 = read_int () in
    i1, i2, i3
  with Failure _ -> 0, 0, 0

(* q3 *)
let rec read_dict () =
  try
    let i = read_int () in
    let name = read_line () in
    (i, name) :: read_dict ()
  with
    | Failure "int_of_string" ->
        print_string "This is not a valid integer. Please try again.";
        print_newline ();
        read_dict ()
    | End_of_file -> []

(* q4 *)
let print_table file n = 
  let o = open_out file in
  let a = Array.init n (fun i -> 
    let init = i + 1 in
    Array.init n (fun i' -> (i' + 1) * init))
  in
  Array.iter (fun row -> 
    Array.iter (fun col -> Printf.sprintf "%-5i" col |> output_string o) row ;
    output_string o "\n"
  ) a ;
  close_out o

(* q5 *)
let count_line file = 
  let i = open_in file in
  let rec helper lines = 
    try
      let _ = input_line i in
      helper (lines + 1)
    with End_of_file -> lines
  in
  let lines = helper 0 in
  close_in i ;
  lines

(* q6 *)
let rec copy_file_ch from_ch to_ch =
  try
    output_string to_ch (input_line from_ch);
    output_string to_ch "\n";
    copy_file_ch from_ch to_ch
  with
    End_of_file -> ()

exception CopyFailed

let copy_file from_name to_name =
  try
    let from_ch = open_in from_name in
    let to_ch = open_out to_name in
    copy_file_ch from_ch to_ch ;
    close_in from_ch ;
    close_out to_ch
  with
    _ -> raise CopyFailed